/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel.model.codes;

import org.zhiqim.kernel.constants.CodeConstants;
import org.zhiqim.kernel.util.Bytes;

/**
 * Base64解码<br>
 * 
 * 根据Base64的字符串，生成对应的字节数组，再通过BASE64表反向找到byte原值。<br>
 * 再根据每4字节一组，每字节去除前2位(4 * 6)转成3个字节(3 * 8)。<br><br>
 * 
 * BASE64表[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/]。
 * 
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class Base64Decoder implements CodeConstants
{
    private static byte[] BASE64_BYTE = new byte[256];//256个byte位置，用于反向取letter位置值
    
    static
    {//初始化一个BASE64有效字节数组，类似于建一个哈希表，通过letter获取byte
        
        //先对所有的byte赋值为-1
        for (int i=0;i<BASE64_BYTE.length;i++)
        {
            BASE64_BYTE[i] = -1;
        }
        
        //对letter赋值为letter的位置值，如A=0,a=26,0=52,+=62,/=63
        for (int i=0;i<_BASE64_BS_.length;i++)
        {
            byte b = _BASE64_BS_[i];
            BASE64_BYTE[b] = (byte)i;
        }
    }
    
    /** Base64解码 */
    public static byte[] decode(byte[] src)
    {
        //第一步，去除不是BASE64表里的字符，包括'='号
        src = removeNonBase64(src);
        if (src.length == 0)
            return new byte[0];
        
        //第二步，取得letter对应的byte原值
        for (int i=0;i<src.length;i++)
        {
            src[i] = BASE64_BYTE[src[i]];
        }
        
        //第三步，每4个字节转换成3个字节
        int length = src.length / 4;
        int remainderLen = src.length % 4;
        
        int destLen = length * 3 + ((remainderLen == 0)?0:((remainderLen == 2)?1:2));//[0,1,2]
        byte[] dest = new byte[destLen];
        
        for (int i=0;i<length;i++)
        {
            int srcIndex = i * 4;
            int destIndex = i * 3;
            
            byte s1 = src[srcIndex];
            byte s2 = src[srcIndex+1];
            byte s3 = src[srcIndex+2];
            byte s4 = src[srcIndex+3];
            
            //将4个字节按网络顺序转成无符号整型，格式为[00000000 S1S1S1 S2S2S2 S3S3S3 S4S4S4]
            int d = (s1 & 0xFF) << 18 | (s2 & 0xFF) << 12 | (s3 & 0xFF) << 6 | (s4 & 0xFF);
            
            dest[destIndex]   = (byte)(d >> 16);
            dest[destIndex+1] = (byte)(d >> 8);
            dest[destIndex+2] = (byte)(d);
        }
        
        if (remainderLen == 0)
            return dest;
        
        //第四步，最剩余字节处理(remainderLen=[2,3])
        int srcIndex = length * 4;
        int destIndex = length * 3;
        if (remainderLen == 2)
        {//还剩2个字节，对应解码为1个字节
            byte s1 = src[srcIndex];
            byte s2 = src[srcIndex+1];
            
            //将2个字节按网络顺序转成无符号整型，格式为[00000000 000000 000000 S1S1S1 S20000]
            int s = (s1 & 0xFF) << 6 | (s2 & 0xFF);
            dest[destIndex] = (byte)(s >> 4);
        }
        else
        {//还剩3个字节，对应解码为2个字节
            byte s1 = src[srcIndex];
            byte s2 = src[srcIndex+1];
            byte s3 = src[srcIndex+2];
            
            //将3个字节按网络顺序转成无符号整型，格式为[00000000 000000 000000 S1S1S1 S2S2S2 S3S300]
            //00000000 000000 0000S1 S1S1S1 000000 000000
            //00000000 000000 000000 0000S2 S2S2S2 000000
            //00000000 000000 000000 0000S2 0000S3 S3S3S3
            //00000000 000000 0000S1 S1S1S1 S2S2S2 S3S3S3
            int s = (s1 & 0xFF) << 12 | (s2 & 0xFF) << 6 | (s3 & 0xFF);
            dest[destIndex]   = (byte)(s >> 10);
            dest[destIndex+1] = (byte)(s >> 2);
        }
                
        return dest;
    }
    
    /** 删除不在BASE64的其他字符，如可能的\r\n */
    private static byte[] removeNonBase64(byte[] src)
    {
        int validLen = 0;
        byte[] valid = new byte[src.length];
        for (byte b : src)
        {
            if (b < '+' || b > 'z')
                continue;//'+'最小=43,'z'最大=122
            
            byte v = BASE64_BYTE[b];
            if (v == -1)
                continue;
            
            valid[validLen++] = b;
        }
        
        return Bytes.getBytes(valid, 0, validLen);
    }
}
